スマートコントラクト入門 | Anonify解体新書6
#LayerX_Newsletter 2021-10-15
前回の内容はこちら
mTLSを使ったEnclave間の通信 | Anonify解体新書5
TL;DR
セキュリティ・プライバシー保護技術Anonifyの主要技術要素について解説する連載記事(全8回)
https://www.anonify.layerx.co.jp
Anonifyで使用されている技術要素を洗い出し重要な技術について簡単なサンプルプログラムを交えて解説する。
前回まではAnonifyで使用されているTEE(Intel SGX)技術について解説してきたが今回からはAnonifyが使用しているブロックチェーン技術について取り上げる
その6では「スマートコントラクト入門」と題して、スマートコントラクトの開発環境を構築する方法と簡単なスマートコントラクトのプログラムを作成する方法について解説する
サンプルプログラムはSolidityとJavaScriptを使用する
ここで使用するコードはすべて独立して動作するのでAnonify自体の知識やAnonifyの動作環境は不要
この記事の中で使用する図は特別な記載がない限り全て筆者が作成したもの
サンプルプログラムはこちら
https://github.com/glassonion1/solidity-101/tree/main/truffle/helloworld
https://github.com/glassonion1/solidity-101/tree/main/truffle/greeter
Anonifyが使用している主な技術要素
TEE(Intel SGX)関連
OCall/ECall
Remote Attestation
crypto_box(NaCl)
データのシーリング
mutual-TLS
ブロックチェーン関連
スマートコントラクト <- 今回解説するのはここ
Web3
ブロックチェーンのおさらい
ブロックチェーン(Blockchain)とは
取引履歴を暗号技術によって過去から1本の鎖のようにつなげ、正確な取引履歴を維持しようとする技術
雑にいうと書き込み専用のデータベース
ブロック(台帳)にトランザクション(取引)データが書き込まれている
全てのブロックが一連の鎖のように繋がっているので取引の順番が保証されている
タイムスタンプ的な役割を果たす
https://scrapbox.io/files/6168d34c6d0062001d7cedb3.svg
ブロックとトランザクションの関係(イーサリアムの場合)
https://scrapbox.io/files/617e82fb8aec7d0022358ecc.svg
パブリック型とプライベート型
誰にでも解放されているパブリック型と特定の企業やサービスに閉じたプライベート型がある
パブリック型
ビットコイン(Bitcoin)、イーサリアム(Ethereum)など
プライベート型
Hyperledger Fabric、Quorum(イーサリアムベース)など
エンタープライズブロックチェーンといわれていることもある
プライベート型ブロックチェーンの実行環境は自前で構築するかKaleidoのようなサービスを使用して構築することができる
分散型台帳とよく表現されるが何が分散なのか
データのコピーが世界中に無数に存在する、だれでもコピーを作れる
パブリックチェーンだとものすごいデータ量で大変だけど
ブロックチェーンのデータが全て記録されているものをフルノードという
1箇所にデータが集約されていない非中央集権的データ
glassonion1.icon データが分散して保存されていると思ってたけど違った
glassonion1.icon シャードチェーンだとブロックが分散されるのかな
データのコピーが大量にある中でどうやってブロックを繋いていくのか
PoW(Proof of Work)やPoS(Proof of Stake)という仕組みでブロックを繋ぐ
マイニングといわれるやつ
PoWは大量の電気を消費するので地球温暖化方面で問題になってる
ブロックチェーンに使用されている技術やブロックチェーンのデータ構造については以下の書籍がおすすめ
徹底理解ブロックチェーン
暗号通貨(資産)とブロックチェーン
ビットコインが取引データの保存にブロックチェーンを採用したため、暗号通貨=ブロックチェーンという認識が広まった
暗号通貨の取引履歴をブロックチェーンに保存している
ブロックチェーンは暗号通貨以外にもいろんな取引に使用できる
glassonion1.icon 暗号通貨とブロックチェーンは分けて考えるのが良いと思ってます
glassonion1.icon マイニング(ブロック生成)報酬が暗号通貨で支払われるので全く無関係と言うわけではないですが
cipepser.icon 資金決済法的には暗号通貨より、暗号資産ですね〜ご参考まで
glassonion1.icon ありがとうございます。法律的には資産なんですね。通貨と表現すると日本円とかと同じ扱いになっちゃうのかな
スマートコントラクト
もとの定義はこちら
「当事者が他の約束を実行する手順まで含んだ、デジタル形式で規定された一連の約束」by Nick Szabo
簡単にいうとブロックチェーン上で動作するプログラム
ブロックチェーン上に処理とその実行条件をあらかじめプログラミングしておくことができる
ブロックチェーンとスマートコントラクトの関係
リレーショナルデータベースにおけるストアドプロシージャみたいなもの
イーサリアムの特徴としてスマートコントラクト機能がある
スマートコントラクト機能を持った他のブロックチェーンプラットフォームもあるがスマートコントラクトといえばイーサリアムなイメージ
スマートコントラクトで使用できるプログラム言語は複数存在する
Solidity、Vyperなど
glassonion1.icon Vyperは瀕死らしい
AnonifyではSolidityを使用している
ブロックチェーン上でプログラムを実行することが前提なのでファイルI/Oやネットワーク接続なんかはできない
イーサリアムにおけるスマートコントラクト
イーサリアムネットワークプロトコルの一部として決定論的に実行される改変不可能なコンピュータプログラム
改変できないというところがブロックチェーンらしい特徴
コンパイルしたスマートコントラクトコードをデプロイするとバイトコードがトランザクションとして登録される
https://scrapbox.io/files/617bb0215ec4ed00226be4d2.svg
アカウントとアドレス
イーサリアムには2種類のアカウントが存在する
EOA(Externally Owned Account)
コントラクトアカウント(Contract account)
アカウントは20バイトのアドレスと状態を持っている
EOAはユーザが使用するアカウント
口座みたいなもの
コントラクトアカウントはコントラクトのコードを持っている
コントラクトのデプロイ時や生成時にアカウントが発行されアドレスとコントラクトが紐づけられる
コントラクトには必ずアドレスが割り振られる
コントラクトのオーナー
コントラクトにはオーナーという概念がある
コントラクトをデプロイしたアカウントをオーナーに設定することが多い
コントラクトのオーナーを他のアカウントに渡すこともできる
オーナーを設定しないコントラクトは誰でも実行できる
コントラクトの実行はオーナーのみに制限するのが望ましい
オーナー周りの実装方法は次回取り上げる
実践Solidityプログラミング | Anonify解体新書7
Anonifyとブロックチェーン
つくば市との実証実験ではAnonifyはイーサリアム型プライベートブロックチェーンを使用した
VOTE FORとLayerX、インターネット投票を見据え、高い秘匿性と非改ざん性を備えた市民意見収集システムをつくば市で実証へ
実行環境としてKaleidoを使用した
クライアントから送られてきたデータをTEE(Intel SGX)で処理をしてブロックチェーンにデータを書き込む
ブロックチェーンにデータを書き込むときにスマートコントラクトを使用した
Anonifyのスマートコントラクトのプログラムはオープンソースとして公開されている
https://github.com/LayerXcom/anonify-contracts
イーサリアムの実行環境いろいろ
メインネット
イーサリアムの本番環境
本物のイーサ(ETH)の取引ができる
テストネット
イーサリアムのテスト環境
プライベートネット
ローカルのコンピュータで実行できる環境
スマートコントラクトの開発環境として利用できる
プライベート型ブロックチェーンのネットワーク
Kaleidoに建てたネットワークなど
スマートコントラクトの開発環境を構築してみる
Ganacheを使ってプライベートネット環境を構築してスマートコントラクトを実行する
RemixやらGethやらsolcやらMetamaskやらいろいろありすぎてわけわからんとなりがち
Truffle + Ganacheがシンプルでおすすめ
最近はHardHatも人気、HardHatについては次回取り上げる
実践Solidityプログラミング | Anonify解体新書7
必要なもの
Node.js
Truffleを実行するのに必要
Truffle
イーサリアムアプリケーションの開発環境
英語でトリュフ(フランス語だとtruffe)のこと、英語読みだとトラッフル
Ganache
Truffleプロジェクトの一部
ローカルで動作するイーサリアムプライベートブロックチェーン
ガナッシュ
Mousseというイーサ2.0用のプライベートブロックチェーン環境もおすすめ
https://note.com/nrryuya/n/n3d9ed5379d67
各種インストールする前にソースコードやデータを置くディレクトリを作成しておく
code: bash
$ mkdir data # Ganacheのデータがこちらに保存される
$ mkdir helloworld # ソースコードをこちらに保存する
$ cd helloworld
Node.jsのインストール
16.11.0をインストールすること
npmは8.0.0
手順は割愛
Truffleのインストール
code: bash
$ npm install -g truffle
... 省略 ...
To address issues that do not require attention, run:
npm audit fix
To address all issues possible, run:
npm audit fix --force
Some issues need review, and may require choosing
a different dependency.
Run npm audit for details.
上記のようにセキュリティ周りの警告が出る場合は以下コマンドで解消すること
code: bash
$ npm i --package-lock-only
$ npm audit fix
$ exec $SHELL -l # Shellの再起動
Truffleプロジェクトの作成
helloworldディレクトリで実行すること
いくつかのコードが自動生成される
code: bash
$ truffle init
Starting init...
================
Copying project files to /Users/xxxxx/solidity-101/helloworld
Init successful, sweet!
Try our scaffold commands to get started:
$ truffle create contract YourContractName # scaffold a contract
$ truffle create test YourTestName # scaffold a test
http://trufflesuite.com/docs
truffle-config.jsの設定
truffle-config.jsはtruffle initで自動生成されたファイル
Ganacheに接続するための設定を追加する
code: truffle-config.js
module.exports = {
networks: {
development: {
host: "127.0.0.1", // ローカルホスト
port: 8545, // Ganacheのデフォルトポート
network_id: "*", // Any network
},
},
};
Ganacheのセットアップ
docker composeで用意する
ganache-coreイメージを使用する(ganache-cliはganache-coreに統合された)
code: docker-compose.yaml
version: "3"
services:
ganache:
image: trufflesuite/ganache-core:latest
ports:
- "8545:8545"
volumes:
- ./data:/ganache_data
Ganacheを起動する
code: bash
$ docker compose up -d
完成したSolidity開発環境の確認
完成したSolidityの開発環境のディレクトリ構成は以下
code: bash
$ tree
solidity-101
├── README.md
├── data
├── docker-compose.yaml
└── helloworld
├── contracts
│   └── Migrations.sol # 自動生成されるコントラクト、どのマイグレーションまで完了しているか記憶してくれる
├── migrations
│   └── 1_initial_migration.js # Migrations.solをデプロイするためのコード、自動生成される
├── test
└── truffle-config.js
SolidityでHello World書いてみる
contractsディレクトリにHelloWorld.solファイルを作成する
code: HelloWorld.sol
pragma solidity >=0.4.22 <0.9.0;
contract HelloWorld {
function greet() external pure returns(string memory) {
return "Hello World!";
}
}
コードの解説
contractはオブジェクト指向言語のクラスに相当する概念
externalは関数のスコープで外部から呼び出し可能という意味
pureはブロックチェーンからデータを読み取ることも書き込むこともないという意味
戻り値のstring memoryは文字列をブロックチェーンに記録しないで返すという意味
コードをコンパイルする
コンパイルが成功するとbuild/contractsディレクトリにバイトコードが生成される
code: bash
$ truffle compile
Compiling your contracts...
===========================
Compiling ./contracts/HelloWorld.sol
Artifacts written to /Users/xxxxx/solidity-101/helloworld/build/contracts
Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
コンパイルしたコードをデプロイする
マイグレーションファイルを作成する
コードをデプロイするにはマイグレーションファイルの作成が必要
2_deploy_hello_world.jsという名前でマイグレーションファイルを作成する
code: 2_deploy_hello_world.js
const HelloWorldContract = artifacts.require('HelloWorld');
module.exports = function(deployer) {
deployer.deploy(HelloWorldContract)
};
デプロイする
Ganacheが起動してないとデプロイできないので注意すること
2回目以降はtruffle migrate --reset
code: bash
$ truffle migrate
Compiling your contracts...
===========================
Everything is up to date, there is nothing to compile.
Starting migrations...
======================
Network name: 'development'
Network id: 1634135548918
Block gas limit: 12000000 (0xb71b00)
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
transaction hash: 0xcbacec63143c2daa776a06d06a2279360d83b8261210f07e3e8a28b1dea6ac1c
Blocks: 0 Seconds: 0
contract address: 0xF3e8295d567c3d048D59058B378eb89AA97F8F72
block number: 1
block timestamp: 1634135560
account: 0x263Eb2FC832a480721501320315fFeE3A68Fe35e
balance: 99.999616114
gas used: 191943 (0x2edc7)
gas price: 2 gwei
value sent: 0 ETH
total cost: 0.000383886 ETH
Saving migration to chain.
Saving artifacts
-------------------------------------
Total cost: 0.000383886 ETH
1634135122_hello_world.js
=========================
Deploying 'HelloWorld'
----------------------
transaction hash: 0x439b6ac9195d547dd488d282348c2506694711df030edd6aed940597866095e0
Blocks: 0 Seconds: 0
contract address: 0x8408acB27E7068c29485BC471C681cb30E62aA73
block number: 3
block timestamp: 1634135562
account: 0x263Eb2FC832a480721501320315fFeE3A68Fe35e
balance: 99.999205152
gas used: 163143 (0x27d47)
gas price: 2 gwei
value sent: 0 ETH
total cost: 0.000326286 ETH
Saving migration to chain.
Saving artifacts
-------------------------------------
Total cost: 0.000326286 ETH
Summary
=======
Total deployments: 2
Final cost: 0.000710172 ETH
デプロイしたコントラクトの動作確認
truffle consoleコマンドで動作確認する
undefinedと表示されても気にしない
code: bash
$ truffle console
truffle(development)> const helloWorld = await HelloWorld.deployed()
undefined
truffle(development)> helloWorld.greet()
'Hello World!'
メッセージを外部から設定できるコントラクトを作成してみる
HelloWorld.solは固定の文字列を返すだけのコントラクトだったが、文字列を外部から設定できるコントラクトを作成してみる
Greeter.solファイルを作成する
code: Greeter.sol
pragma solidity >=0.4.22 <0.9.0;
contract Greeter {
string private _message = "Hello World!";
function greet() external view returns(string memory) {
return _message;
}
function setMessage(string calldata message) external {
_message = message;
}
}
コードの解説
コントラクトに文字列型の_messageメンバを定義する
greetは設定された_messageメンバの値を返す関数
viewはブロックチェーンにデータを書き込むことはないがブロックチェーンのデータを読み取るという意味
setMessageは_messageメンバに引数で指定された文字列を設定する関数
calldataは関数の引数を格納する領域のこと
テストを書いてコントラクトの動作を確認する
HelloWorldではtruffle consoleを使って動作確認したが、JavaScriptでテストを書いて確認することもできる
テストを実行するとコンパイルからデプロイまで実行してくれる
マイグレーションファイルを作成すること
testディレクトリにgreeter-test.jsという名前でファイルを作成する
code: greeter-test.js
const GreeterContract = artifacts.require('Greeter');
contract('Greeter', () => {
// コントラクトのインスタンス
let instance;
// テストの前処理、コントラクトのインスタンスを生成する
before(async () => {
instance = await GreeterContract.deployed();
});
// greete関数のテスト
it('should get message correctly', async () => {
const expected = 'Hello World!';
const actual = await instance.greet();
assert.equal(actual, expected, 'greeted with Hello World!');
});
// setMessage関数のテスト
it('should update message correctly', async () => {
const expected = 'Hi there!';
await instance.setMessage(expected);
const actual = await instance.greet();
assert.equal(actual, expected, 'greeting was not updated');
});
});
コードの解説
コントラクトのテストはJavaScriptで行う
Mochaベース、Truffle独自の拡張のcontract句を使用する
before関数でデプロイされているコントラクトのインスタンスを取得する
テストの実行
コンパイルからデプロイまで一通りやってくれる
code: bash
$ truffle test
Using network 'development'.
Compiling your contracts...
===========================
Compiling ./contracts/Greeter.sol
Artifacts written to /var/folders/p3/vphzjsyn58q3v2kc5j_bzrg00000gn/T/test--62233-Bd3YEAvdBBrx
Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
Contract: Greeter
✓ should get message correctly
✓ should update message correctly (1256ms)
2 passing (1s)
まとめ
ブロックチェーンは書き込み専用のデータベースみたいなもの
スマートコントラクトはRDBにおけるストアドプロシージャみたいなもの
スマートコントラクトの開発環境はTruffle + Ganacheがおすすめ
スマートコントラクトのプログラムはSolidityで書く
スマートコントラクトのテストはJavaScriptで書く
スマートコントラクトのコードはコンパイル後にブロックチェーンにデプロイが必要
スマートコントラクトの開発環境はいろいろな選択肢があってわりと迷いました。スマートコントラクトの開発をするだけならTruffleでほとんど賄えるのでおすすめです。Solidityのコードはかなり癖が強いのではじめは面食らいましたが、Solidity独自のキーワードからスマートコントラクトの思想みたいなものが読み取れたので楽しくプログラミングできるようになりました。今回は簡単なプログラムしか紹介できなかったので次回はもう少し実践的なSolidityプログラムを紹介したいと思います。(文責・藤田)
Anonify解体新書 | 連載一覧(全8回)
Rust SGXプログラミングの基本 | Anonify解体新書1
実行の完全性を実現するSGX Remote Attestation | Anonify解体新書2
E2EEを実現するための暗号技術 | Anonify解体新書3
E2EEを支える安全な鍵管理 | Anonify解体新書4
mTLSを使ったEnclave間の通信 | Anonify解体新書5
スマートコントラクト入門 | Anonify解体新書6
実践Solidityプログラミング | Anonify解体新書7
TEE + ブロックチェーン | Anonify解体新書8